Scenario #9690: A Global Admin Can Create a Read Only Api Key Subject

The endpoint-scope *:read makes an API-key read-only: it allows all GET endpoints under /api/, but nothing which changes data. With the global ADMIN role granted to its API_KEY subject, such an API-key can read everything, e.g. for reporting or monitoring, without any risk of modifying data.

Properties

Given

name value
subjectUuid a91c0009-0000-0000-0000-000000000009
subjectName readonly.key

Create the read-only API_KEY Subject

HTTP POST "/api/rbac/subjects" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  <<EOF
{
  "uuid" : "a91c0009-0000-0000-0000-000000000009",
  "name" : "readonly.key",
  "type" : "API_KEY",
  "scopes" : [ "*:read" ]
}
EOF
=> status: 201 CREATED a91c0009-0000-0000-0000-000000000009
{
  "uuid" : "a91c0009-0000-0000-0000-000000000009",
  "name" : "readonly.key",
  "organization" : "readonly",
  "type" : "API_KEY",
  "apiKey" : "hsak_readonly.key.420fc143a935a8ae347474fd249ad4ebfbe1e878ae22e240956dc49f5b9f36ae",
  "scopes" : [ "*:read" ],
  "expiresAt" : null
}

Prerequisite: Resolve the UUID of the global ADMIN role

The grant API needs the UUID of the role which we want to grant.

HTTP GET "/api/rbac/roles?name=rbac.global%23global%3AADMIN" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }`
=> status: 200 OK 
[ {
  "uuid" : "2366934b-3ba5-453a-a070-78698db295e0", // globalAdminRoleUuidToGrant
  "object.uuid" : "a8842cb7-7284-468a-9e78-07e6fea8bc98",
  "objectTable" : "rbac.global",
  "objectIdName" : "global",
  "roleType" : "ADMIN",
  "roleName" : "rbac.global#a8842cb7-7284-468a-9e78-07e6fea8bc98:ADMIN",
  "roleIdName" : "rbac.global#global:ADMIN"
} ]

Grant the global ADMIN role to the API_KEY Subject

HTTP POST "/api/rbac/grants" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  -H 'Hostsharing-Assumed-Roles: rbac.global#global:ADMIN' \
  <<EOF
{
  "assumed" : true,
  "grantedRole.uuid" : "2366934b-3ba5-453a-a070-78698db295e0", // globalAdminRoleUuidToGrant
  "granteeSubject.uuid" : "a91c0009-0000-0000-0000-000000000009"
}
EOF
=> status: 201 CREATED 2366934b-3ba5-453a-a070-78698db295e0 // globalAdminRoleUuidToGrant

Verify the read-only API-key can use GET endpoints, e.g. list all memberships

HTTP GET "/api/hs/office/memberships" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 200 OK 
[
  {
    "uuid" : "640248e3-8ae0-4f09-87a2-1fed91a6eb2c", // Membership: M-3101000 - Test AG
    "partner" : {
      "uuid" : "17f118e3-af4e-44b2-a512-6a2a331570a4", // Partner: P-31010 - Test AG
      "partnerNumber" : "P-31010",
      "partnerRel" : {
        "uuid" : "22b0c1b5-5b3b-4cd1-8c56-464f71deeac3", // partnerRelationUuid
        "anchor" : {
          "uuid" : "882bda1d-4e5e-42f8-afdd-b9741d1c1f4e",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Hostsharing eG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "holder" : {
          "uuid" : "9e12338f-c1ea-4237-9d64-f085a84850c6", // Person: Test AG
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Test AG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "type" : "PARTNER",
        "mark" : null,
        "contact" : {
          "uuid" : "eef1dec6-8687-4615-91ae-3fbbcc48dd34", // Contact: Test AG - China
          "caption" : "Test AG - China",
          "postalAddress" : {
            "country" : "China",
            "province" : "Guangdong Province",
            "city" : "Dongguan City",
            "street" : "No.2 Commercial Second Street",
            "district" : "Niushan Wei Wu",
            "department" : "Executive Board",
            "building" : "Thi Chi Koh Building"
          },
          "emailAddresses" : {
            "main" : "norden@test-ag.example.org"
          },
          "phoneNumbers" : {
            "phone" : "++15 999 654321"
          }
        }
      },
      "details" : {
        "uuid" : "2afc94e0-29c7-4396-8e96-fbb46213e0fa",
        "registrationOffice" : "Registergericht Hamburg",
        "registrationNumber" : "1234567",
        "birthName" : null,
        "birthPlace" : null,
        "birthday" : null,
        "dateOfDeath" : null
      }
    },
    "mainDebitor" : null,
    "memberNumber" : "M-3101000",
    "memberNumberSuffix" : "00",
    "validFrom" : "2020-10-15",
    "validTo" : "2023-12-31",
    "status" : "CANCELLED",
    "membershipFeeBillable" : true
  },
  {
    "uuid" : "1ebb88e2-0219-4cd3-aa99-b8c7e449e65e",
    "partner" : {
      "uuid" : "8e9e2793-24ea-4d1d-9f56-17e724475ba9",
      "partnerNumber" : "P-10001",
      "partnerRel" : {
        "uuid" : "99cd7791-d436-4490-b935-f7096a957224",
        "anchor" : {
          "uuid" : "882bda1d-4e5e-42f8-afdd-b9741d1c1f4e",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Hostsharing eG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "holder" : {
          "uuid" : "50df9b54-6221-4116-ad23-49d2876d8100", // Person: First GmbH
          "personType" : "LEGAL_PERSON",
          "tradeName" : "First GmbH",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "type" : "PARTNER",
        "mark" : null,
        "contact" : {
          "uuid" : "df451bad-85e7-4d82-95df-3ab8ea3804ab", // Contact: contact-admin@firstcontact.example.com
          "caption" : "first contact",
          "postalAddress" : {
            "country" : "Germany"
          },
          "emailAddresses" : {
            "main" : "contact-admin@firstcontact.example.com"
          },
          "phoneNumbers" : {
            "phone_office" : "+49 123 1234567"
          }
        }
      },
      "details" : {
        "uuid" : "14597f55-b714-44be-ba3c-810b6626d98b",
        "registrationOffice" : "Hamburg",
        "registrationNumber" : "RegNo123456789",
        "birthName" : null,
        "birthPlace" : null,
        "birthday" : null,
        "dateOfDeath" : null
      }
    },
    "mainDebitor" : null,
    "memberNumber" : "M-1000101",
    "memberNumberSuffix" : "01",
    "validFrom" : "2022-10-01",
    "validTo" : "2024-12-30",
    "status" : "CANCELLED",
    "membershipFeeBillable" : true
  },
  {
    "uuid" : "b2947aa1-cd46-419d-8278-b5278856dc30",
    "partner" : {
      "uuid" : "d6856fa4-1cad-4648-a196-a381810b8e02", // partnerUuid
      "partnerNumber" : "P-10002",
      "partnerRel" : {
        "uuid" : "7b6e2a87-209b-477f-96a4-1ea5d0b1fd9b",
        "anchor" : {
          "uuid" : "882bda1d-4e5e-42f8-afdd-b9741d1c1f4e",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Hostsharing eG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "holder" : {
          "uuid" : "eabf7140-e674-4dd2-be51-e97fdb317d59",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Peter Smith - The Second Hand and Thrift Stores-n-Shipping e.K.",
          "salutation" : null,
          "title" : null,
          "givenName" : "Peter",
          "familyName" : "Smith"
        },
        "type" : "PARTNER",
        "mark" : null,
        "contact" : {
          "uuid" : "784a23db-869f-44e5-ab5c-960fa62a28fa",
          "caption" : "second contact",
          "postalAddress" : {
            "country" : "Germany"
          },
          "emailAddresses" : {
            "main" : "contact-admin@secondcontact.example.com"
          },
          "phoneNumbers" : {
            "phone_office" : "+49 123 1234567"
          }
        }
      },
      "details" : {
        "uuid" : "04ea7d51-ad55-4766-9a3a-4465021c45c9",
        "registrationOffice" : "Hamburg",
        "registrationNumber" : "RegNo123456789",
        "birthName" : null,
        "birthPlace" : null,
        "birthday" : null,
        "dateOfDeath" : null
      }
    },
    "mainDebitor" : null,
    "memberNumber" : "M-1000202",
    "memberNumberSuffix" : "02",
    "validFrom" : "2022-10-01",
    "validTo" : "2025-12-31",
    "status" : "CANCELLED",
    "membershipFeeBillable" : true
  },
  "..."
]

Verify the read-only API-key cannot write, e.g. not upsert a subject

HTTP PUT "/api/rbac/subjects/a91c0009-0000-0000-0000-000000000009" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY" \
  <<EOF
{
  "name" : "hsh-never_updated",
  "type" : "USER"
}
EOF
=> status: 403 FORBIDDEN 
{
  "path" : "/api/rbac/subjects/a91c0009-0000-0000-0000-000000000009",
  "statusCode" : 403,
  "statusPhrase" : "Forbidden",
  "message" : "ERROR: [403] API-key scopes do not allow PUT /api/rbac/subjects/a91c0009-0000-0000-0000-000000000009"
}

generated on 2026-08-10 04:34:37 for branch HEAD